﻿namespace Microsoft.VisualStudio.ServiceModel.DomainServices.Tools
{
    using System;
    using System.CodeDom;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Reflection;
    using System.ServiceModel.DomainServices.Hosting;
    using System.Linq;

    internal class BusinessLogicContext
    {
        private Microsoft.VisualStudio.ServiceModel.DomainServices.Tools.ContextData _contextData;
        private Type _contextType;
        private List<BusinessLogicEntity> _entities;
        private string _name;
        private static int uniqueContextID;
        private const string UserDataHelperMethods = "HelperMethods";

        public BusinessLogicContext(Type contextType, string name)
        {
            this._contextType = contextType;
            this._name = name;
        }

        protected virtual bool CanGeneratePropertyOfType(Type type)
        {
            type = TypeUtilities.GetElementType(type);
            if (TypeUtilities.IsPredefinedType(type))
            {
                return true;
            }
            foreach (BusinessLogicEntity entity in this.Entities)
            {
                if (entity.ClrType == type)
                {
                    return true;
                }
            }
            return false;
        }

        protected virtual CodeTypeDeclaration CreateBusinessLogicClass(CodeGenContext codeGenContext, CodeNamespace codeNamespace, string className)
        {
            CodeTypeDeclaration declaration = CodeGenUtilities.CreateTypeDeclaration(className, codeNamespace.Name);
            declaration.BaseTypes.Add(BusinessLogicClassConstants.DomainServiceTypeName);
            return declaration;
        }

        protected virtual IEnumerable<BusinessLogicEntity> CreateEntities()
        {
            return new BusinessLogicEntity[0];
        }

        protected virtual bool GenerateAdditionalMetadataClasses(CodeGenContext codeGenContext, string optionalSuffix, BusinessLogicEntity entity)
        {
            return false;
        }

        protected void GenerateBusinessLogicClass(CodeGenContext codeGenContext, string className, string namespaceName)
        {
            string str;
            if (codeGenContext == null)
            {
                throw new ArgumentNullException("codeGenContext");
            }
            if (string.IsNullOrEmpty(className))
            {
                throw new ArgumentNullException("className");
            }
            if (string.IsNullOrEmpty(namespaceName))
            {
                throw new ArgumentNullException("namespaceName");
            }
            CodeNamespace orGenNamespace = codeGenContext.GetOrGenNamespace(namespaceName);
            CodeTypeDeclaration declaration = this.CreateBusinessLogicClass(codeGenContext, orGenNamespace, className);
            orGenNamespace.Types.Add(declaration);
            if (this.ContextType == null)
            {
                str = Resources.BusinessLogicClass_Class_Remarks_Empty;
            }
            else
            {
                str = string.Format(CultureInfo.CurrentCulture, Resources.BusinessLogicClass_Class_Remarks, new object[] { this.ContextType.Name });
            }
            declaration.Comments.Add(new CodeCommentStatement(str, false));
            if (this.ContextType != null)
            {
                str = codeGenContext.IsCSharp ? Resources.BusinessLogicClass_RequiresAuthentication_CSharp : Resources.BusinessLogicClass_RequiresAuthentication_VB;
                declaration.Comments.Add(new CodeCommentStatement(str, false));
            }
            if (this.IsClientAccessEnabled)
            {
                CodeAttributeDeclaration declaration2 = CodeGenUtilities.CreateAttributeDeclaration(BusinessLogicClassConstants.EnableClientAccessAttributeTypeName);
                declaration.CustomAttributes.Add(declaration2);
            }
            else
            {
                declaration.Comments.Add(new CodeCommentStatement(Resources.BusinessLogicClass_EnableClientAccess_Comment));
            }
            foreach (BusinessLogicEntity entity in from e in this.Entities
                orderby e.Name
                select e)
            {
                if (entity.IsIncluded)
                {
                    CodeGenUtilities.AddImportIfNeeded(orGenNamespace, entity.ClrType.Namespace);
                    this.GenerateEntityDomainOperationEntries(codeGenContext, declaration, entity);
                }
            }
            Dictionary<string, CodeTypeMember> helperMemberDictionary = GetHelperMemberDictionary(declaration);
            foreach (string str2 in from s in helperMemberDictionary.Keys
                orderby s
                select s)
            {
                declaration.Members.Add(helperMemberDictionary[str2]);
            }
            if (this.IsODataEndpointEnabled)
            {
                codeGenContext.AddReference(typeof(ODataEndpointFactory).Assembly.FullName);
            }
        }

        public GeneratedCode GenerateBusinessLogicClass(string language, string className, string namespaceName, string rootNamespace)
        {
            using (CodeGenContext context = new CodeGenContext(language, rootNamespace))
            {
                this.GenerateBusinessLogicClass(context, className, namespaceName);
                return context.GenerateCode();
            }
        }

        protected virtual void GenerateDeleteMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
        }

        internal void GenerateEntityDomainOperationEntries(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
            CodeMemberMethod method = this.GenerateSelectMethod(codeGenContext, businessLogicClass, entity);
            if ((method != null) && this.IsODataEndpointEnabled)
            {
                CodeAttributeDeclaration declaration = new CodeAttributeDeclaration(new CodeTypeReference("Query"), new CodeAttributeArgument[] { new CodeAttributeArgument("IsDefault", new CodePrimitiveExpression(true)) });
                method.CustomAttributes.Add(declaration);
            }
            if (entity.IsEditable)
            {
                this.GenerateInsertMethod(codeGenContext, businessLogicClass, entity);
                this.GenerateUpdateMethod(codeGenContext, businessLogicClass, entity);
                this.GenerateDeleteMethod(codeGenContext, businessLogicClass, entity);
            }
        }

        internal static void GenerateHelperMemberIfNecessary(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, string helperMemberName, Func<CodeTypeMember> generatorCallback)
        {
            Dictionary<string, CodeTypeMember> helperMemberDictionary = GetHelperMemberDictionary(businessLogicClass);
            if (!helperMemberDictionary.ContainsKey(helperMemberName))
            {
                CodeTypeMember member = generatorCallback();
                if (member != null)
                {
                    helperMemberDictionary[helperMemberName] = member;
                }
            }
        }

        protected virtual void GenerateInsertMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
        }

        internal bool GenerateMetadataClass(CodeGenContext codeGenContext, string optionalSuffix, Type type)
        {
            if (TypeUtilities.GetAssociatedMetadataType(type) != null)
            {
                return false;
            }
            string name = type.Name;
            string namespaceName = type.Namespace;
            bool flag = !string.IsNullOrEmpty(optionalSuffix);
            if (flag)
            {
                name = name + optionalSuffix;
                namespaceName = namespaceName + optionalSuffix;
            }
            CodeNamespace orGenNamespace = codeGenContext.GetOrGenNamespace(namespaceName);
            if (flag)
            {
                CodeGenUtilities.AddImportIfNeeded(orGenNamespace, type.Namespace);
            }
            string typeName = name + "Metadata";
            string str4 = name + "." + typeName;
            CodeTypeDeclaration declaration = null;
            declaration = CodeGenUtilities.CreateTypeDeclaration(name, namespaceName);
            declaration.IsPartial = true;
            declaration.TypeAttributes = TypeAttributes.Public;
            declaration.Comments.Add(new CodeCommentStatement(string.Format(CultureInfo.CurrentCulture, Resources.BusinessLogicClass_Entity_Partial_Class_Remarks, new object[] { typeName, name }), false));
            CodeAttributeDeclaration declaration2 = CodeGenUtilities.CreateAttributeDeclaration(BusinessLogicClassConstants.MetadataTypeAttributeTypeName);
            CodeAttributeArgument argument = new CodeAttributeArgument(new CodeTypeOfExpression(str4));
            declaration2.Arguments.Add(argument);
            declaration.CustomAttributes.Add(declaration2);
            CodeTypeDeclaration buddyClass = CodeGenUtilities.CreateTypeDeclaration(typeName, namespaceName);
            buddyClass.TypeAttributes = TypeAttributes.Sealed | TypeAttributes.NestedAssembly;
            bool flag2 = false;
            buddyClass.Comments.Add(new CodeCommentStatement(string.Format(CultureInfo.CurrentCulture, Resources.Buddy_Class_Remarks, new object[] { type.Name })));
            string text = codeGenContext.IsCSharp ? Resources.Buddy_Class_Remarks_CSharp : Resources.Buddy_Class_Remarks_VB;
            buddyClass.Comments.Add(new CodeCommentStatement(text, false));
            CodeConstructor constructor = new CodeConstructor {
                Attributes = MemberAttributes.Private
            };
            constructor.Comments.Add(new CodeCommentStatement(Resources.BusinessLogicClass_Private_Ctor_Comment));
            buddyClass.Members.Add(constructor);
            foreach (PropertyInfo info in from p in type.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                orderby p.Name
                select p)
            {
                Type propertyType = info.PropertyType;
                if ((propertyType.IsVisible && (info.GetGetMethod() != null)) && this.CanGeneratePropertyOfType(propertyType))
                {
                    CodeGenUtilities.AddImportIfNeeded(orGenNamespace, info.PropertyType.Namespace);
                    CodeSnippetTypeMember member = CodeGenUtilities.CreateAutomaticPropertyDeclaration(codeGenContext, buddyClass, info, !string.IsNullOrEmpty(orGenNamespace.Name));
                    buddyClass.Members.Add(member);
                    flag2 = true;
                }
            }
            if (flag2)
            {
                orGenNamespace.Types.Add(declaration);
                declaration.Members.Add(buddyClass);
            }
            return flag2;
        }

        protected bool GenerateMetadataClasses(CodeGenContext codeGenContext, string optionalSuffix)
        {
            bool flag = false;
            if (this.NeedToGenerateMetadataClasses)
            {
                foreach (BusinessLogicEntity entity in from e in this.Entities
                    orderby e.Name
                    select e)
                {
                    if (entity.IsIncluded)
                    {
                        flag |= this.GenerateMetadataClass(codeGenContext, optionalSuffix, entity.ClrType);
                        flag |= this.GenerateAdditionalMetadataClasses(codeGenContext, optionalSuffix, entity);
                    }
                }
            }
            return flag;
        }

        public GeneratedCode GenerateMetadataClasses(string language, string rootNamespace, string optionalSuffix)
        {
            using (CodeGenContext context = new CodeGenContext(language, rootNamespace))
            {
                if (this.GenerateMetadataClasses(context, optionalSuffix))
                {
                    return context.GenerateCode();
                }
            }
            return new GeneratedCode();
        }

        protected virtual CodeMemberMethod GenerateSelectMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
            return null;
        }

        protected virtual void GenerateUpdateMethod(CodeGenContext codeGenContext, CodeTypeDeclaration businessLogicClass, BusinessLogicEntity entity)
        {
        }

        protected static Dictionary<string, CodeTypeMember> GetHelperMemberDictionary(CodeTypeDeclaration businessLogicClass)
        {
            Dictionary<string, CodeTypeMember> dictionary = null;
            if (businessLogicClass.UserData.Contains("HelperMethods"))
            {
                return (businessLogicClass.UserData["HelperMethods"] as Dictionary<string, CodeTypeMember>);
            }
            dictionary = new Dictionary<string, CodeTypeMember>();
            businessLogicClass.UserData["HelperMethods"] = dictionary;
            return dictionary;
        }

        public Microsoft.VisualStudio.ServiceModel.DomainServices.Tools.ContextData ContextData
        {
            get
            {
                if (this._contextData == null)
                {
                    Microsoft.VisualStudio.ServiceModel.DomainServices.Tools.ContextData data = new Microsoft.VisualStudio.ServiceModel.DomainServices.Tools.ContextData {
                        Name = this.NameAndDataAccessLayerName,
                        IsClientAccessEnabled = true,
                        ID = uniqueContextID++
                    };
                    this._contextData = data;
                }
                return this._contextData;
            }
            set
            {
                this._contextData = value;
            }
        }

        public Type ContextType
        {
            get
            {
                return this._contextType;
            }
        }

        public virtual string DataAccessLayerName
        {
            get
            {
                return null;
            }
        }

        public IEnumerable<BusinessLogicEntity> Entities
        {
            get
            {
                if (this._entities == null)
                {
                    try
                    {
                        this._entities = new List<BusinessLogicEntity>(this.CreateEntities());
                    }
                    catch (Exception exception)
                    {
                        if (exception is OutOfMemoryException)
                        {
                            throw;
                        }
                        this._entities = new List<BusinessLogicEntity>();
                    }
                    this._entities.Sort((Comparison<BusinessLogicEntity>) ((x, y) => string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase)));
                }
                return this._entities;
            }
        }

        public IEnumerable<EntityData> EntityDataItems
        {
            get
            {
                return (from ble in this.Entities select ble.EntityData);
            }
        }

        public bool IsClientAccessEnabled
        {
            get
            {
                return this.ContextData.IsClientAccessEnabled;
            }
        }

        public bool IsODataEndpointEnabled
        {
            get
            {
                return this.ContextData.IsODataEndpointEnabled;
            }
        }

        public string Name
        {
            get
            {
                return this.ContextData.Name;
            }
        }

        public string NameAndDataAccessLayerName
        {
            get
            {
                string str = this._name;
                string dataAccessLayerName = this.DataAccessLayerName;
                if (!string.IsNullOrEmpty(dataAccessLayerName))
                {
                    return string.Format(CultureInfo.CurrentCulture, Resources.BusinessLogicClass_Name_And_Technology, new object[] { str, dataAccessLayerName });
                }
                return str;
            }
        }

        internal virtual bool NeedToGenerateMetadataClasses
        {
            get
            {
                return true;
            }
        }
    }
}

